/**
 *  
 */
package research.sbookmarking.input;

import java.util.*;
import org.afox.util.*;
import research.sbookmarking.*;
import org.restlet.data.*;

//
/**
 * This is an abstract class. Each child is required to implement the {@link execute} method.  This class
 * conntains the command that will be executed against Connotea.org 
 * <p> 
 * @author <A HREF="mailto:aatleung@gmail.com">Albert Leung</a>
 * @version 1.0
 * 
 */
public abstract class Command {
	
	private DataContainer result = null;
	private List commands = null;
	private String queryName = null;
	
	// by default the result of the query will not be saved in DataContainer
	private boolean saveResults = false;
	
	//the default starting value to retrieve at
	private int start=0;
	//the default maximum number records to retrieve each query
	private int limit=10;
	
	/**
	 * This constructor can only be called by its subclass.
	 * <p> 
	 * @param queryName contains the type of query to be execute against Connotea.org.
	 * @param commands contains the list of parameters that will be query. 
	 */
	protected Command (String queryName, List commands){
		this.queryName = queryName;
		this.commands = commands;
	}


	/**
	 * This constructor can only be called by its subclass.
	 * <p> 
	 * @param queryName contains the type of query to be execute against Connotea.org.
	 * @param commands contains the list of parameters that will be query. 
	 * @param limit contains the maximum number of records that query each time. 
	 * @param start contains the starting record to start querying from.
	 */
	protected Command (String queryName, List commands, Integer limit, Integer start){
		setQueryName(queryName);
		setCommands(commands);
		setLimit(limit);
		setStart(start);
	}

	protected String getQueryName() {
		return this.queryName;
	}

	protected void setQueryName(String queryName){
		this.queryName = queryName;
	}

	protected List getCommands() {
		return commands;
	}
	
	protected void setCommands(List commands){
		this.commands = commands;
	}

	protected int getLimit(){
		return limit;
	}
	
	protected void setLimit(Integer limit){
		this.limit = limit.intValue();
	}
	
	protected int getStart(){
		return start;
	}

	protected void setStart(Integer start){
		this.start = start.intValue();
	}
/*
	protected List getParameter() {
		return parameter;
	}
*/
	/**
	 * This method needs to be implement in its children.  This method contains the procedures
	 * required to query Connotea.org 
	 */
	public abstract void execute();
	
	/**
	 * Returns the results from the data query of Connotea.org
	 * @return a {@link DataContainer} that contains the data extracted from Connotea.org.
	 */
	public DataContainer getResult() {
		return result;
	}

	protected void setResult(DataContainer data) {
		this.result=data;
	}
	
	protected void copy(List results, Iterator list) {
		while (list.hasNext()){
			results.add(list.next());
		}
	}
	
	//This method should only be called by subclass and nothing else
	public Object processURL(String url) {
		return new Reference(url);
	}
	
	//This method should only be called by subclass and nothing else
	public Object processUser(String username){
		return username;
	}
	
	//This method should only be called by subclass and nothing else
	public Object processTag (String subject){
		return new Tag(subject);
	}
	
	//This method should only be called by subclass and nothing else
	public Object processDate (String date){
		String[]dateParts = date.split("-");
		Calendar cal = Calendar.getInstance();
		cal.set(Integer.parseInt(dateParts[0]), Integer.parseInt(dateParts[1]), 
				Integer.parseInt(dateParts[2])); 
		return cal.getTime();
	}

	protected int getAll (List results, Object obj, String command, 
						  Object param1, int limit, int start){
		List temp = null;
		int errorNo = 0;
		int oldLimit=limit;
		boolean error=false;
		boolean doubled=false;
		ArrayList param = new ArrayList();
		
		do {
			try {
				if ((error) && (doubled)){
					limit=oldLimit;
					error=false;
					doubled=false;
				}
				
				param.add(param1);
				param.add(new Integer(limit));
				param.add(new Integer(start));
				temp = (List)RMethod.invoke(obj, command, param);
				copy (results,temp.iterator());

				errorNo = oldLimit + start;
				if (oldLimit==temp.size()){
					System.out.println ("No of records received for " + param1 + ": " + 
							results.size() + " out of " + (errorNo));
					start+=limit;
					param.clear();
				}
			}
			catch (java.lang.OutOfMemoryError x) {
				System.err.println ("Not enough memmory: " + x.getCause());
				System.exit(-1);
			}
			catch (Exception e) {
				//System.out.println ("Error Message: " + e.getMessage());
				System.out.println ("Error Message: " + e.getCause());
				System.out.println ("Getting the next set of data.");
				System.out.println ("No of records received for " + param1 + ": " + 
						results.size() + " out of " + (start+1));
				error=true;
				if (errorNo==oldLimit+start){
					doubled=true;
					start++;
				}
				
				errorNo=oldLimit+start;
				limit=1;
				param.clear();
			}
			
		} while (temp.size()>=limit);

		System.out.println ("Total no. of records received for " + param1 + ": " + 
				results.size() + " out of " + (temp.size()+start));

		return start+temp.size();
	}


	protected boolean isSaveResults() {
		return saveResults;
	}


	protected void setSaveResults(boolean saveResults) {
		this.saveResults = saveResults;
	}
}
